package logica;

import java.util.Iterator;
import logica.persistencia.EntidadPersistente;
import memento.IMemento;
import memento.MementoEntidadLogica;

import java.util.LinkedList;
import java.util.List;

public class Entidad extends ObjetoDiagramaConAtributo implements Comparable<Object>{
	
	private List<Identificador> identificadores;
	private List<Entidad> idExternos;
	private TipoDeEntidad tipo;
	
	public Entidad(String nombre) throws SinNombreException
	{
		super(nombre);
		
		this.setTipo(TipoDeEntidad.NO_DEFINIDO);
		idExternos = new LinkedList<Entidad>();
		identificadores = new LinkedList<Identificador>();
	}
	
	@Override
	public List<Entidad> getReferenciaAEntidadesExternas() {
		return idExternos;
	}
	
	@Override
	public void agregarseAlDiagrama(Diagrama diagrama) throws IdExternoInexistenteException, EntidadRepetidaException {
		diagrama.agregarEntidadAlDiagrama(this);
	}
	
	@Override
	public void verificarSiSePuedeAgregarAlDiagrama(Diagrama diagrama) throws IdExternoInexistenteException, EntidadRepetidaException 
	{
		diagrama.verificarSiEntidadRepetida(this);
		diagrama.verificarSiDependenciasExternasExisten(this);
	}
		
	public void agregarIdentificador(Identificador identificador) throws AtributoIdentificadorFaltanteException, EntidadIdentificadoraFaltanteException, IdentificadorVacioException 
	{
		verificarSiIdentificadorVacio(identificador);
		verificarSiAtributoEsta(identificador);
		verificarSiEntidadEsta(identificador);
		
		this.getIdentificadores().add(identificador);
	}

	private void verificarSiIdentificadorVacio(Identificador identificador) throws IdentificadorVacioException 
	{
		if (identificador.getAtributos().isEmpty() && identificador.getEntidades().isEmpty())
			throw new IdentificadorVacioException("El identificador de la entidad esta vacio.");
	}

	private void verificarSiEntidadEsta(Identificador identificador) throws EntidadIdentificadoraFaltanteException 
	{
		Iterator<Entidad> itEntidad = identificador.getEntidades().iterator();
		
		while (itEntidad.hasNext())
		{
			try
			{
				Entidad entidad = itEntidad.next();
				verificarSiIdExternoRepetido(entidad);
				throw new EntidadIdentificadoraFaltanteException("El identificador de la entidad contiene una referencia a una entidad de la cual no depende. Ver: "+ entidad);
			}
			catch (IdExternoRepetidoException e) {}
		}
	}

	private void verificarSiAtributoEsta(Identificador identificador) throws AtributoIdentificadorFaltanteException 
	{
		Iterator<Atributo> itAtributo = identificador.getAtributos().iterator();
		
		while (itAtributo.hasNext())
		{
			try
			{
				Atributo atributo = itAtributo.next();
				verificarSiAtributoRepetido(atributo);
				throw new AtributoIdentificadorFaltanteException("El identificador contiene un atributo que no forma parte de la entidad. Ver: "+ atributo);
			}
			catch (AtributoRepetidoException e) {}
		}
	}
	
	public void agregarIdentificadores(List<Identificador> identificadores) throws AtributoIdentificadorFaltanteException, EntidadIdentificadoraFaltanteException, IdentificadorVacioException 
	{
		Iterator<Identificador> iter = identificadores.iterator();
		
		while (iter.hasNext()) 
			agregarIdentificador(iter.next());
	}
	
	public void agregarEntidadExterna(Entidad entidad) throws IdExternoRepetidoException  
	{
		if (entidad == null)
			return;
		
		verificarSiIdExternoRepetido(entidad);
		
		if (entidad == this)
			throw new IdExternoRepetidoException("Una entidad no se puede tener así misma como identificador externa.");
			
		this.getReferenciaAEntidadesExternas().add(entidad);
	}
	
	public void agregarEntidadesExternas(List<Entidad> entidades) throws IdExternoRepetidoException 
	{
		Iterator<Entidad> iter = entidades.iterator();
		
		while (iter.hasNext()) 
			agregarEntidadExterna(iter.next());
	}
		
	public String toString() {
		return this.getNombre();
	}
	
	public boolean equals(Object entidad)
	{
		if (entidad.getClass() != this.getClass())
			return false;
		
		return ((Entidad)entidad).getNombre().compareToIgnoreCase(this.getNombre())==0;
	}

	public void setTipo(TipoDeEntidad tipo) {
		this.tipo = tipo;
	}

	public TipoDeEntidad getTipo() {
		return tipo;
	}
	
	private void verificarSiIdExternoRepetido(Entidad entidad) throws IdExternoRepetidoException 
	{
		Iterator<Entidad> iteradorEntidad = this.getReferenciaAEntidadesExternas().iterator();

		while (iteradorEntidad.hasNext()) 
		{
			if (entidad.equals(iteradorEntidad.next()))
				throw new IdExternoRepetidoException("Una entidad no puede tener identificadores externos repetidos. Ver: " + entidad);
		}
	}

	public EntidadPersistente getObjetoDiagramaPersistente() {
		return new EntidadPersistente(this);
	}

	public List<Identificador> getIdentificadores() {
		return identificadores;
	}
	
	public void setReferenciasExternas(List<Entidad> entidades) throws IdExternoRepetidoException
	{
		this.idExternos = new LinkedList<Entidad>();
		this.agregarEntidadesExternas(entidades);
	}
	
	public void setIdentificadores(List<Identificador> identificadores) throws AtributoIdentificadorFaltanteException, EntidadIdentificadoraFaltanteException, IdentificadorVacioException 
	{
		this.identificadores = new LinkedList<Identificador>();
		this.agregarIdentificadores(identificadores);
	}

	@Override
	public int compareTo(Object o) 
	{
		Entidad entidad = (Entidad)o;        
		        
		return -entidad.getNombre().compareToIgnoreCase(this.getNombre());
	}

	@Override
	public IMemento getMementoEspecializado() {
		return new MementoEntidadLogica(this);
	}

	@Override
	public void saveMemento(IMemento memento) {
		memento.cargarMemento();
	}

	@Override
	public void eliminaseDelDiagrama() {
		this.getDiagrama().eliminarEntidadDelDiagrama(this);
	}

	@Override
	public boolean tieneEstaEntidad(Entidad entidad)
	{
		for (Entidad ent: this.getReferenciaAEntidadesExternas())
		{
			if (ent == entidad)
				return true;
		}
		
		return false;
	}
	
	public boolean soyEntidadDebil() {
		return this.getReferenciaAEntidadesExternas().size() != 0;
	}
}
